#include "encode.h"

#include <sys/select.h>

#include "mpi_venc.h"
#include "hi_common.h"
#include "hi_comm_video.h"
#include "hi_comm_sys.h"
#include "mpi_sys.h"
#include "hi_comm_vb.h"
#include "mpi_vb.h"
#include "hi_comm_vpss.h"
#include "mpi_vpss.h"
#include "mpi_vgs.h"

#include "util.h"

struct hiDEMO_MEMBUF_S
{
    VB_BLK  hBlock;
    VB_POOL hPool;
    HI_U32  u32PoolId;
	HI_U32 u32BlkSize;
    HI_U64 u64PhyAddr;
    HI_U64 u64VirAddr;
    HI_S32  s32Mdev;
} g_stMem1;

static int encode_chn = 0;

int _pack_jpeg(VIDEO_FRAME_INFO_S *frame,char** buf, int* len);

// 分配vb内存，并关联VIDEO_FRAME_INFO_S
static HI_S32 _myalloc(int w, int h, VIDEO_FRAME_INFO_S* frame);

// 释放内存
static HI_S32 _myfree();

static int _venc_send_frame(VIDEO_FRAME_INFO_S *frame);

static int _venc_get_stream(VENC_PACK_S *pstPack,char** jpegBuf, int* jpegBufLen);


int _venc_send_frame(VIDEO_FRAME_INFO_S *frame)
{
	if(encode_chn == -1)
	{
		printf("illegal channel!!!\n");
		return -1;
	}
	int s32Ret;
	VENC_CHN_ATTR_S attr;	
	VIDEO_FRAME_INFO_S *ptempFrame = (VIDEO_FRAME_INFO_S *)frame;
Again:
	s32Ret = HI_MPI_VENC_GetChnAttr(encode_chn, &attr);
	if(s32Ret != HI_SUCCESS)
	{
		printf("HI_MPI_VENC_GetChnAttr error with %#x\n",s32Ret);
		return -1;
	}

	if(attr.stVencAttr.u32PicWidth != ptempFrame->stVFrame.u32Width
			|| attr.stVencAttr.u32PicHeight != ptempFrame->stVFrame.u32Height)
	{
		attr.stVencAttr.u32PicWidth = ptempFrame->stVFrame.u32Width;
		attr.stVencAttr.u32PicHeight = ptempFrame->stVFrame.u32Height;		
		HI_MPI_VENC_SetChnAttr(encode_chn, &attr);
	}
	s32Ret = HI_MPI_VENC_SendFrame(encode_chn,ptempFrame,2000);
	if(s32Ret != HI_SUCCESS)
	{
		encode_create(encode_chn,ptempFrame->stVFrame.u32Width,ptempFrame->stVFrame.u32Height);
		goto Again;
	}
	return 0;
}

int _venc_get_stream(VENC_PACK_S *pstPack,char** jpegBuf, int* jpegBufLen)
{
	HI_S32 s32Ret;
	VENC_CHN_STATUS_S stStat;
    VENC_STREAM_S stStream;
	int Intfd = HI_MPI_VENC_GetFd(encode_chn);//HANDLE
	
	fd_set read_fds;
	struct timeval timeout;
	timeout.tv_sec = 2;
    timeout.tv_usec = 0;
	int itry = 0;

	FD_ZERO(&read_fds);
	FD_SET(Intfd,&read_fds);
	while(itry++ < 5)
	{
		s32Ret = select(Intfd+1, &(read_fds), NULL, NULL, &timeout);
	 	if (s32Ret == 0)//timeout,continue
		{
			continue;
		}
		if(s32Ret < 0)
		{
			printf("call select error,return\n");
			return -1;
		}
	}
	if(!FD_ISSET(Intfd, &read_fds))
	{
		printf("FD is not set ,return...\n");
		return -1;
	}
	s32Ret = HI_MPI_VENC_QueryStatus(encode_chn, &stStat);				
	if(s32Ret != HI_SUCCESS)
	{
		printf("@@@@@@##################HI_MPI_VENC_Query:0x%x###########\n", s32Ret);
		return -1;
	}

	stStream.pstPack = pstPack;
	stStream.u32PackCount = stStat.u32CurPacks; 
	s32Ret = HI_MPI_VENC_GetStream(encode_chn, &stStream, 2000);
	if (HI_SUCCESS != s32Ret)
	{
		printf("HI_MPI_VENC_GetStream failed with %#x!\n", s32Ret);
		return -1;
	}
	int streamLen = 0;	
	HI_U32 StartNalu;
	for(StartNalu = 0;StartNalu < stStream.u32PackCount; StartNalu++)
	{
		streamLen += (stStream.pstPack[StartNalu].u32Len - stStream.pstPack[StartNalu].u32Offset);
	}

	*jpegBufLen = streamLen;
	*jpegBuf = (char *)malloc(streamLen);
	if(jpegBuf == NULL)
	{
		printf("call MemoryAlloc error\n");
		HI_MPI_VENC_ReleaseStream(encode_chn, &stStream);
		return -1;
	}
	
	HI_U8 *paddr = NULL;
	HI_U32 u32len = 0;	
	HI_U8 *pBuffer = (HI_U8 *)(*jpegBuf);
						
	for(StartNalu = 0;StartNalu < stStream.u32PackCount; StartNalu++)
	{
		paddr = stStream.pstPack[StartNalu].pu8Addr + stStream.pstPack[StartNalu].u32Offset;
		u32len = stStream.pstPack[StartNalu].u32Len - stStream.pstPack[StartNalu].u32Offset;
		memcpy(pBuffer, paddr, u32len);
		pBuffer += u32len;		
	}
	
	s32Ret = HI_MPI_VENC_ReleaseStream(encode_chn, &stStream);
	if (s32Ret)
	{
		printf("HI_MPI_VENC_ReleaseStream:0x%x\n", s32Ret);//FATAL ERROR or NOT?
		free(jpegBuf);
		jpegBuf = NULL;
		return -1;
	}
		
	return 0;
}


HI_S32 _myalloc(int nImageWidth, int nImageHeight, VIDEO_FRAME_INFO_S* pstFrmInfo)
{
    VB_POOL_CONFIG_S stVbPoolCfg;
    HI_U32 u32LumaSize = 0, u32ChrmSize = 0;
	if (pstFrmInfo == NULL){
		return HI_FAILURE;
	}
	// Although we need w*h*1.5 alloc more buffer does not impact
	g_stMem1.u32BlkSize = nImageWidth*nImageHeight*2;
    memset(&stVbPoolCfg, 0, sizeof(VB_POOL_CONFIG_S));
    stVbPoolCfg.u64BlkSize  = g_stMem1.u32BlkSize;
    stVbPoolCfg.u32BlkCnt   = 1;
    stVbPoolCfg.enRemapMode = VB_REMAP_MODE_NONE;
	g_stMem1.hPool = HI_MPI_VB_CreatePool(&stVbPoolCfg);
	if ( VB_INVALID_POOLID == g_stMem1.hPool)
	{
		__log_mesg("create vb err\n");
		return HI_FAILURE;
	}

	g_stMem1.hBlock = HI_MPI_VB_GetBlock(g_stMem1.hPool,g_stMem1.u32BlkSize,HI_NULL);
	if (VB_INVALID_HANDLE ==g_stMem1.hBlock )
	{
		__log_mesg("get vb block err\n");
		_myfree();
		return HI_FAILURE;
	}

	g_stMem1.u64PhyAddr = HI_MPI_VB_Handle2PhysAddr(g_stMem1.hBlock);
	if (HI_NULL == g_stMem1.u64PhyAddr)
	{
		__log_mesg("blk to physaddr err\n");
		_myfree();
		return HI_FAILURE;
	}

	g_stMem1.u64VirAddr = (HI_U64)HI_MPI_SYS_Mmap(g_stMem1.u64PhyAddr,g_stMem1.u32BlkSize);
    if (g_stMem1.u64VirAddr == 0)
    {
        __log_mesg("Mem dev may not open\n");
        _myfree();
        return HI_FAILURE;
    }
	memset(pstFrmInfo,0,sizeof(VIDEO_FRAME_INFO_S));
    pstFrmInfo->stVFrame.u64PhyAddr[0] = g_stMem1.u64PhyAddr;

	u32LumaSize = nImageWidth *  nImageHeight;
	u32ChrmSize = nImageWidth *  nImageHeight >> 2;
	
    pstFrmInfo->stVFrame.u64PhyAddr[0] = g_stMem1.u64PhyAddr;
    pstFrmInfo->stVFrame.u64PhyAddr[1] = pstFrmInfo->stVFrame.u64PhyAddr[0] + u32LumaSize;
	pstFrmInfo->stVFrame.u64PhyAddr[2] = pstFrmInfo->stVFrame.u64PhyAddr[1] + u32ChrmSize;

    pstFrmInfo->stVFrame.u64VirAddr[0] = g_stMem1.u64VirAddr;
    pstFrmInfo->stVFrame.u64VirAddr[1] = (HI_U64) pstFrmInfo->stVFrame.u64VirAddr[0] + u32LumaSize;
	pstFrmInfo->stVFrame.u64VirAddr[2] = (HI_U64) pstFrmInfo->stVFrame.u64VirAddr[1] + u32ChrmSize;

    pstFrmInfo->stVFrame.u32Width  = nImageWidth;
    pstFrmInfo->stVFrame.u32Height = nImageHeight;
	pstFrmInfo->stVFrame.u32Stride[0] = nImageWidth;
	pstFrmInfo->stVFrame.u32Stride[1] = nImageWidth;

    pstFrmInfo->stVFrame.enField = VIDEO_FIELD_FRAME;
    pstFrmInfo->stVFrame.enCompressMode = COMPRESS_MODE_NONE;
    pstFrmInfo->stVFrame.enPixelFormat = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
    //pstFrmInfo->stVFrame.enPixelFormat = PIXEL_FORMAT_YUV_400;
    pstFrmInfo->stVFrame.u64PTS = 0;
    //pstDst->stVFrame.u32TimeRef = pstSrc->stVFrame.u32TimeRef;
    pstFrmInfo->u32PoolId = g_stMem1.hPool;

	return HI_SUCCESS;
}

HI_S32 _myfree()
{
    if(HI_NULL != g_stMem1.u64VirAddr)
    {
        HI_MPI_SYS_Munmap((HI_VOID*)g_stMem1.u64VirAddr, g_stMem1.u32BlkSize);
        g_stMem1.u64PhyAddr = HI_NULL;
    }
    if(VB_INVALID_HANDLE != g_stMem1.hBlock)
    {
        HI_MPI_VB_ReleaseBlock(g_stMem1.hBlock);
        g_stMem1.hBlock = VB_INVALID_HANDLE;
    }

    if (VB_INVALID_POOLID != g_stMem1.hPool)
    {
        HI_MPI_VB_DestroyPool(g_stMem1.hPool);
        g_stMem1.hPool = VB_INVALID_POOLID;
    }
    return HI_SUCCESS;
}

int _pack_jpeg(VIDEO_FRAME_INFO_S *stFrame,char** jpegBuf, int* jpegBufLen )
{
	if(_venc_send_frame(stFrame) != 0)//送编码
	{
		printf("call Public_send_frame_to_venc[%d] error\n",encode_chn);		
		return -1;
	}

	VENC_PACK_S *pstpack = (VENC_PACK_S *)malloc(10*sizeof(VENC_PACK_S));
	if(pstpack == NULL)
	{
		return -1;
	}
	
	if(_venc_get_stream(pstpack, jpegBuf, jpegBufLen) !=0)//获取编码之后的JPEG图片
	{
		printf("call Public_venc_get_stream[%d] error\n",encode_chn);	
		free(pstpack);
		return -1;
	}

	free(pstpack);
	return 0;
}

int encode_create(int chn,int width,int height)
{
    HI_S32 s32Ret;
    VENC_RECV_PIC_PARAM_S stRecvParam;
	VENC_CHN_ATTR_S stVencChnAttr;
    memset(&stVencChnAttr, 0x00, sizeof(stVencChnAttr));
	stVencChnAttr.stVencAttr.enType = PT_JPEG;
	stVencChnAttr.stVencAttr.u32MaxPicWidth  = width;
	stVencChnAttr.stVencAttr.u32MaxPicHeight = height;
	stVencChnAttr.stVencAttr.u32PicWidth  = width;
	stVencChnAttr.stVencAttr.u32PicHeight = height;
	stVencChnAttr.stVencAttr.u32BufSize = width * height * 2;	
    stVencChnAttr.stVencAttr.u32Profile = 0;
	stVencChnAttr.stVencAttr.bByFrame = HI_TRUE;
	stVencChnAttr.stVencAttr.stAttrJpege.bSupportDCF = HI_FALSE;
	stVencChnAttr.stVencAttr.stAttrJpege.stMPFCfg.u8LargeThumbNailNum = 0;	
	HI_MPI_VENC_StopRecvFrame(chn);
	HI_MPI_VENC_DestroyChn(chn);

	s32Ret = HI_MPI_VENC_CreateChn(chn, &stVencChnAttr);
	if(s32Ret != HI_SUCCESS)
	{
		printf("HI_MPI_VENC_CreateChn [%d] faild with %#x!\n",chn, s32Ret);
	    return s32Ret;
	}

#if 1
		//彩转灰功能
		VENC_CHN_PARAM_S Param;
		HI_MPI_VENC_GetChnParam(chn,&Param);
		Param.bColor2Grey = HI_TRUE;
		HI_MPI_VENC_SetChnParam(chn,&Param);
#endif	

	stRecvParam.s32RecvPicNum = -1;
	s32Ret = HI_MPI_VENC_StartRecvFrame(chn,&stRecvParam);
	if(s32Ret != HI_SUCCESS)
	{
		printf("HI_MPI_VENC_StartRecvPic [%d] faild with %#x!\n",chn, s32Ret);
	    return s32Ret;
	}
    encode_chn = chn;
    return HI_SUCCESS;
}

char* encode_data(char* data, int width, int height, int* len)
{
    VIDEO_FRAME_INFO_S stfrm;
    _myalloc(width, height, &stfrm);
    if (data == NULL) return NULL;
    memcpy((void*)stfrm.stVFrame.u64VirAddr[0], data, height*width);
	char* buff = NULL;
	if (_pack_jpeg(&stfrm, &buff, len)) return NULL;
    _myfree();
    return buff;
}